from apscheduler.schedulers.background import BackgroundScheduler
import time
import asyncio
import logging
from datetime import datetime

from loguru import logger
from sqlalchemy.orm import Session
from typing import Dict, Any

from config.Database import get_db
from config.Env import SystemUserInfo
from schemas.Submitinfo import SubmitInfoUpdate
from services.SubmitInfoServe import SubmitInfoServe
from Util.ActivityUtil import get_detail, apply_activity
from Util.AuthUtil import get_token
from Util.MailUtil import send_mail
from services.UserService import UserServe

# 存储所有定时任务
tasks = {}
UserServe = UserServe()


async def enroll_activity(activity_id: int, submit_info_id: int, username: str, password: str, email: str,
                          activity_detail: Dict[str, Any]):
    """报名活动的具体实现
    
    Args:
        activity_id: 活动ID
        username: 用户名
        password: 密码
        email: 邮箱地址
    
    Returns:
        报名结果
    """
    db = next(get_db())
    try:
        # 重新获取token，防止过期
        token = await get_token(username, password)
        allUserInfo = await UserServe.save_user_info(token, db)
        if not token:
            error_msg = f"活动 {activity_detail.get('name')} 报名失败: 无法获取用户token"
            logger.error(error_msg)
            send_mail(email, "第二课堂活动报名失败", error_msg)
            SubmitInfoServe.update(db, submit_info_id,
                                   SubmitInfoUpdate(nickName=allUserInfo.name, activityName=activity_detail.get('name'),
                                                    isDelete=True, isSuccess=False, remark='无法获取用户token'))
            return

        # 尝试报名，最多尝试10次，每次间隔500ms
        max_attempts = 10
        for attempt in range(1, max_attempts + 1):
            try:
                # 执行报名操作
                result = await apply_activity(activity_id, token)

                # 获取报名结果信息
                if result['data'] is None:
                    msg = result['msg']
                    is_success = False
                else:
                    msg = "ok"  # 假设成功时返回ok
                    is_success = True

                # 构建邮件内容
                if is_success:
                    status = "成功"

                    message = "恭喜您，报名成功！请在手机上查看报名信息。"
                else:
                    status = "失败"
                    reason = ""
                    if msg == "已报名该活动":
                        reason = "您已经报名过该活动"
                    elif msg == "院系受限":
                        reason = "您所在的院系不能参加此活动"
                    elif msg == "报名方式受限":
                        reason = "报名方式受限, 报名需要团队报名"
                    else:
                        reason = msg
                    # TODO 报名失败还需要把自动签到签退的信息给删除掉
                    message = f"很遗憾，报名失败。原因：{reason}"

                email_subject = f"报名{status} {activity_detail.get('name')} "
                email_content = f"""
                尊敬的用户 {username}：
                
                您的第二课堂活动报名结果如下：
                活动id: {activity_id}
                活动名称: {activity_detail.get('name')}
                报名状态: {status}
                系统返回信息: {msg}
                {message}
                
                此邮件由系统自动发送，请勿回复。
                """

                # 发送邮件通知
                send_mail(email, email_subject, email_content)

                # 报名完成，取消该活动的定时任务
                task_key = f"{username}_{activity_id}"
                if task_key in tasks:
                    del tasks[task_key]

                logger.info(f"活动 {activity_id} 报名{status}，系统返回: {msg}，尝试次数: {attempt}")
                SubmitInfoServe.update(db, submit_info_id, SubmitInfoUpdate(nickName=allUserInfo.name,
                                                                            activityName=activity_detail.get('name'),
                                                                            isDelete=True, isSuccess=is_success,
                                                                            remark=result['msg'],applyAt=datetime.now()))
                return result

            except Exception as e:
                logger.warning(f"活动 {activity_id} 报名尝试 {attempt}/{max_attempts} 失败: {str(e)}")
                SubmitInfoServe.update(db, submit_info_id, SubmitInfoUpdate(nickName=allUserInfo.name,
                                                                            activityName=activity_detail.get('name'),
                                                                            isDelete=True, isSuccess=False,
                                                                            remark='代码出现报错',applyAt=datetime.now()))

                if attempt < max_attempts:
                    await asyncio.sleep(0.5)  # 等待500ms后重试
                else:
                    # 所有尝试都失败，发送失败通知
                    error_msg = f"活动 {activity_id} 报名失败，已尝试 {max_attempts} 次: {str(e)}"
                    send_mail(email, "第二课堂活动报名失败", error_msg)
                    # 报名完成，取消该活动的定时任务
                    task_key = f"{username}_{activity_id}"
                    if task_key in tasks:
                        del tasks[task_key]
                    return None
    finally:
        # 确保关闭数据库会话
        db.close()


async def schedule_enrollment(submit_info_id: int, activity_id: int, wait_seconds: float, username: str, password: str,
                              email: str, activity_detail: Dict[str, Any]):
    """
    定时任务调度函数
    
    Args:
        activity_id: 活动ID
        wait_seconds: 等待时间（秒）
        username: 用户名
        password: 密码
        email: 邮箱地址
    """
    db = next(get_db())
    try:
        token = await get_token(username, password)
        allUserInfo = await UserServe.save_user_info(token, db)
        SubmitInfoServe.update(db, submit_info_id,SubmitInfoUpdate(nickName=allUserInfo.name, activityName=activity_detail.get('name'),isDelete=True, isSuccess=False, remark='等待报名'))

        # 等待到报名时间
        await asyncio.sleep(wait_seconds)
        # 执行报名
        await enroll_activity(activity_id, submit_info_id, username, password, email, activity_detail)
    except asyncio.CancelledError:
        logger.info(f"活动 {activity_id} 的定时任务已取消")
        SubmitInfoServe.update(db, submit_info_id, SubmitInfoUpdate(isDelete=True,remark='任务取消'))
    except Exception as e:
        logger.error(f"活动 {activity_id} 的定时任务出错: {str(e)}")
        SubmitInfoServe.update(db, submit_info_id, SubmitInfoUpdate(isDelete=True,remark='任务取消'))
        # 发送错误通知邮件
        send_mail(
            email,
            f"第二课堂活动 {activity_id} 报名失败通知",
            f"很抱歉，在为您安排活动 {activity_id} 的报名任务时出现错误: {str(e)}"
        )
    finally:
        # 确保关闭数据库会话
        db.close()


async def add_task(activity_id: int, enroll_time: int, submit_info_id: int, username: str, password: str, email: str,
                   activity_detail: Dict[str, Any]):
    """添加定时任务
    
    Args:
        activity_id: 活动ID
        enroll_time: 报名时间（毫秒时间戳）
        username: 用户名
        password: 密码
        email: 邮箱地址
    """
    # 获取数据库会话
    db = next(get_db())
    try:
        # 获取提交信息
        submit_info = SubmitInfoServe.get_by_id(db, submit_info_id)
        if not submit_info:
            logger.error(f"无法找到ID为 {submit_info_id} 的提交信息")
            return

        # 计算等待时间（秒）
        now = datetime.now()
        enroll_datetime = datetime.fromtimestamp(enroll_time / 1000)
        wait_seconds = (enroll_datetime - now).total_seconds()

        # 准备邮件内容
        activity_name = activity_detail.get('name', f'活动 {activity_id}')
        email_subject = f"第二课堂活动自动报名已安排 - {activity_name}"

        # 生成任务唯一标识
        task_key = f"{username}_{activity_id}"
        
        # 检查是否已经存在该活动的任务
        if task_key in tasks:
            logger.info(f"用户 {username} 的活动 {activity_id} 任务已经存在，跳过")
            return

        if wait_seconds <= 0:
            # 如果已经到了报名时间，立即执行
            logger.info(f"活动 {activity_id} 已经开始报名，立即执行报名")
            asyncio.create_task(
                enroll_activity(activity_id, submit_info_id, username, password, email, activity_detail))
        else:

            # 创建定时任务
            logger.info(f"活动 {activity_id} 将在 {enroll_datetime} 开始报名，等待 {wait_seconds} 秒")

            # 计算可读的等待时间
            hours = int(wait_seconds // 3600)
            minutes = int((wait_seconds % 3600) // 60)
            seconds = int(wait_seconds % 60)

            wait_time_str = ""
            if hours > 0:
                wait_time_str += f"{hours}小时"
            if minutes > 0:
                wait_time_str += f"{minutes}分钟"
            if seconds > 0 or (hours == 0 and minutes == 0):
                wait_time_str += f"{seconds}秒"

            # 只有当isPush为0时才发送邮件
            if not submit_info.isPush:
                # 发送定时报名通知
                email_content = f"""
                尊敬的用户 {username}:
                
                您好！
                
                您的第二课堂活动【{activity_name}】自动报名任务已成功安排。
                
                活动ID: {activity_id}
                活动名称: {activity_name}
                预计报名时间: {enroll_datetime.strftime('%Y-%m-%d %H:%M:%S')}
                等待时间: {wait_time_str}
                
                系统将在活动开始报名时自动为您执行报名操作，报名结果将通过邮件通知您，请留意邮箱。
                
                此邮件由系统自动发送，请勿回复。
                """

                # 发送邮件通知
                send_mail(email, email_subject, email_content)

                # 更新isPush字段为1
                SubmitInfoServe.update(db, submit_info_id, SubmitInfoUpdate(id=submit_info_id, isPush=True))
                logger.info(f"已向用户 {username} 发送活动 {activity_id} 的报名安排通知")
            else:
                logger.info(f"用户 {username} 的活动 {activity_id} 已经发送过报名安排通知，跳过")

            tasks[task_key] = asyncio.create_task(schedule_enrollment(
                submit_info_id, activity_id, wait_seconds, username, password, email, activity_detail
            ))
    finally:
        # 确保关闭数据库会话
        db.close()


async def apply_job():
    """
    定时任务主函数，检查数据库中的报名请求并创建相应的定时任务
    
    此函数会：
    1. 从数据库中查询提交的任务
    2. 然后根据每一行的任务ID，查询活动的详细信息
    3. 根据详细信息中的报名时间和提交信息中的用户凭据注册定时任务
    4. 定时任务将在适当的时间调用报名函数
    5. 报名结果（成功或失败）将通过邮件通知用户
    """
    logger.info(f"apply_job 定时任务执行：当前时间 {time.strftime('%Y-%m-%d %H:%M:%S')}")

    # 获取数据库会话
    db = next(get_db())
    try:
        # 查询所有未处理的提交信息
        submit_infos = SubmitInfoServe.get_all(db)

        for submit_info in submit_infos:
            # 解析ids字符串为列表
            try:
                # 尝试解析字符串形式的数组，例如 "[285560, 285548]"
                import ast
                ids_list = ast.literal_eval(submit_info.ids)

                # 如果不是列表，则转换为列表
                if not isinstance(ids_list, list):
                    ids_list = [ids_list]
            except (ValueError, SyntaxError):
                # 如果解析失败，尝试直接使用ids作为单个ID
                ids_list = [submit_info.ids]

            # 为每个ID创建单独的任务
            for activity_id in ids_list:
                try:
                    # 获取活动详情
                    # 使用系统管理员token查询活动详情

                    activity_detail = await get_detail(activity_id, SystemUserInfo.get_token())

                    if not activity_detail:
                        logger.warning(f"无法获取活动 {activity_id} 的详细信息")
                        continue

                    # 获取报名时间
                    enroll_time = activity_detail.get('enrollStartTime')
                    if not enroll_time:
                        logger.warning(f"活动 {activity_id} 没有报名时间信息")
                        continue

                    # 添加定时任务
                    await add_task(
                        int(activity_id),
                        enroll_time,
                        submit_info.id,
                        submit_info.username,
                        submit_info.password,
                        submit_info.email,
                        activity_detail
                    )

                except Exception as e:
                    logger.error(f"定时任务执行出错: {str(e)}", exc_info=True)

            # 标记任务为已处理（如果需要）
            # SubmitInfoServe.update(db, submit_info.id, {"isProcessed": True})


    except Exception as e:
        logger.error(f"定时任务执行出错: {str(e)}", exc_info=True)
    finally:
        # 确保关闭数据库会话
        db.close()
